#include <core/types.h>
#include <core/io.h>
#include <core/irq.h>
#include <core/init.h>
#include <arch/arm.h>
#include <core/bitops.h>
#include <autoconf.h>
//#include <arch/at91sam926x.h>

#define  PIO_IRQ_OFFSET  2


static void At91AicAck(UINT32 nIrq)
{
	At91AicWrite(AIC_ICCR, 1 << nIrq);	
}

static void At91AicMask(UINT32 nIrq)
{
	At91AicWrite(AIC_IDCR, 1 << nIrq);
}

static void At91AicMaskAck(UINT32 nIrq)
{
	At91AicMask(nIrq);
	At91AicAck(nIrq);
}

static void At91AicUnmask(UINT32 nIrq)
{
	At91AicWrite(AIC_IECR, 1 << nIrq);
}

//static int	At91SetTrigger(UINT32, UINT32);


static struct IntCtrl gAt91Aic = {
	.Ack		= At91AicAck,
	.Mask		= At91AicMask,
	.MaskAck    = At91AicMaskAck,
	.Unmask		= At91AicUnmask,
//	.SetTrigger	= At91AicSetTrigger,
};


static void At91PioMask(UINT32 nIrq)
{
	UINT32 nPioIdx, nPioPin;

	nIrq -= 32;
	nPioIdx = nIrq >> 5;
	nPioPin = nIrq & 0x1f;

	WriteLong(1 << nPioPin, PIO_BASE(nPioIdx) + PIO_IDR);
}

static void At91PioUnmask(UINT32 nIrq)
{
	UINT32 nPioIdx, nPioPin;

	nIrq -= 32;
	nPioIdx = nIrq >> 5;
	nPioPin = nIrq & 0x1f;

	WriteLong(1 << nPioPin, PIO_BASE(nPioIdx) + PIO_IER);
}

static struct IntCtrl gAt91PioInt = {
	.Mask   = At91PioMask,
	.Unmask = At91PioUnmask,
};


UINT32 ReadIrqNum(void)
{
	return At91AicRead(AIC_IVR);
}

void __INIT__ At91AicInit(void)
{
	int nIrq;


	for(nIrq = 0; nIrq < 32; nIrq++)
	{
		At91AicWrite(AIC_SVR(nIrq), nIrq);
		At91AicWrite(AIC_SMR(nIrq), (0 << 5) | 0);

		IrqCtrlAssociate(nIrq, &gAt91Aic);
		IrqSetHandler(nIrq, IrqHandleLevel, 0);
	}

//	At91AicWrite(AIC_SPU, MAX_IRQ_NUM);
//	At91AicWrite(AIC_DCR, 0);

//	At91AicWrite(AIC_IDCR, ~0UL);
//	At91AicWrite(AIC_ICCR, ~0UL);
}

static void At91PioIrqParse(struct IrqDesc *pDesc, UINT32 nIrq)
{
	UINT32 dwPioStat;
	UINT32 nPioIdx, nPioPin;


	nPioIdx = nIrq - PIO_IRQ_OFFSET;
	dwPioStat = ReadLong(PIO_BASE(nPioIdx) + PIO_ISR);
	dwPioStat &= ReadLong(PIO_BASE(nPioIdx) + PIO_IMR);
//	printf("%s(): stat = 0x%08x\n", __FUNCTION__, dwPioStat);

	for (nPioPin = 0; nPioPin < 32; nPioPin++)
	{
		if (dwPioStat & (1 << nPioPin))
		{
			DoIRQ(32 + 32 * nPioIdx + nPioPin);
		}
	}
}

int __INIT__ At91sam926xInitIrq(void)
{
	UINT32 i, j;
	UINT32 nIrq, nPioIdx, dwMask;


	At91AicInit();

	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 32; j++)
		{
			nIrq = 32 + ((i << 5) | j);

			IrqCtrlAssociate(nIrq, &gAt91PioInt);
			IrqSetHandler(nIrq, IrqHandleSimple, 0);
		}

		IrqSetHandler(PIO_IRQ_OFFSET + i, At91PioIrqParse, 1);
	}

	IrqEnable(); // fixme

	return 0;
}


